home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / et / et3_0-a1.lha / et3 / src / ShellTView.C < prev    next >
C/C++ Source or Header  |  1992-08-05  |  16KB  |  721 lines

  1. #ifdef __GNUG__
  2. #pragma implementation
  3. #endif
  4.  
  5. #include "ShellTView.h"
  6.  
  7. #include "Class.h"
  8. #include "Iterator.h"
  9. #include "OrdColl.h"
  10. #include "ByteArray.h"
  11. #include "StyledText.h"
  12. #include "System.h"
  13. #include "String.h"
  14. #include "Alert_e.h"
  15. #include "Menu.h"
  16. #include "MenuBar.h"
  17. #include "Window.h"
  18. #include "WindowSystem.h"
  19. #include "Math.h"
  20.  
  21. char *gDefaultArgv[] = { "/bin/csh", 0 };
  22.  
  23. const int cPttyBufSize      = 8192,
  24.       cHighWaterMark    = 50000,
  25.       cLowWaterMark     = 4000,
  26.       cESC              = 27;
  27.  
  28. //---- output cursor (reversed caret) --------------------------------------
  29.  
  30. const int crevCaretHeight = 4,
  31.       crevCaretWidth  = 7;
  32.  
  33. static u_short revCaretBits[]= {
  34. #   include "images/revCaret.image"
  35. };
  36.  
  37. static SmartBitmap gRevCaretImage(Point(crevCaretWidth,crevCaretHeight),
  38.                                 revCaretBits);
  39.  
  40. static RGBColor *gCaretColor;
  41.  
  42. //---- class PttyInputHandler --------------------------------------------------
  43.  
  44. class PttyInputHandler : public SysEvtHandler {
  45.     FILE *fp;
  46.     ShellTextView *tv;
  47. public:
  48.     MetaDef(PttyInputHandler);
  49.     PttyInputHandler(FILE *f, ShellTextView *t) : SysEvtHandler(fileno(f))
  50.     { fp= f; tv= t; }
  51.     void Notify(SysEventCodes, int)
  52.     { tv->Received(); }
  53. };
  54.  
  55. NewMetaImpl(PttyInputHandler, SysEvtHandler, (TP(tv)));
  56.  
  57. //---- ShellZombieHandler ------------------------------------------------------
  58.  
  59. class ShellZombieHandler : public SysEvtHandler {
  60.     ShellTextView *sh;
  61. public:
  62.     MetaDef(ShellZombieHandler);
  63.     ShellZombieHandler(int pid, ShellTextView *t) : SysEvtHandler(pid)
  64.     { sh= t;}
  65.     void Notify(SysEventCodes, int)
  66.     { sh->SlaveDied(); }
  67. };
  68.  
  69. NewMetaImpl(ShellZombieHandler, SysEvtHandler, (TP(sh)));
  70.  
  71. //---- class ShellTextView -----------------------------------------------------
  72.     
  73. NewMetaImpl(ShellTextView, TextView, (TP(outputCursor), TP(inputCursor), 
  74.                TP(zombieHandler), TP(pttyinp), TB(pendingCR),
  75.                TB(doReveal), TP(noEchoBuffer), T(lastSubmit),
  76.                T(maxLogSize), T(state), TP(pttycstr), 
  77.                TSP(argv), 0));
  78.                
  79. ShellTextView::ShellTextView(
  80.     EvtHandler *eh, Rectangle r, Text *t,
  81.     char *name, char **args, bool cons, 
  82.     TextViewFlags fl, Point b
  83. ) : TextView(eh, r, t, TRUE, fl, b, eTViewLeft) 
  84. {
  85.     Init(TRUE, name, args, cons);
  86. }   
  87.  
  88. void ShellTextView::Init(bool reveal, char *name, char **args, bool cons)
  89. {
  90.     doReveal= reveal;
  91.     outputCursor= new Mark(0, 0, eStateNone, (eMarkFlags)eMarkNone);
  92.     inputCursor= new Mark(0, 0, eStateNone, (eMarkFlags)eMarkNone);
  93.  
  94.     linebuf= 0;
  95.     pendingCR= FALSE;
  96.     inReceive= FALSE;
  97.     maxLogSize= cHighWaterMark;
  98.     noEchoBuffer= new GapText(20);
  99.     inpQueue= new OrdCollection;
  100.     GetText()->AddMark(outputCursor);
  101.     GetText()->AddMark(inputCursor);
  102.     gSystem->Setenv("TERM", GetTerm());
  103.     gSystem->Setenv("TERMCAP", GetTermCap());
  104.     pttycon= 0;
  105.     Start(name, args, cons);
  106. }
  107.  
  108. ShellTextView::~ShellTextView()
  109. {
  110.     Term();
  111. }
  112.  
  113. void ShellTextView::Term()
  114. {
  115.     Object *op;
  116.     if (text) {
  117.     op= text->RemoveMark(outputCursor);
  118.     SafeDelete(op);
  119.     op= text->RemoveMark(inputCursor);
  120.     SafeDelete(op);
  121.     }
  122.     if (pttyinp) {
  123.     pttyinp->Remove();
  124.     pttyinp= 0;
  125.     }
  126.     if (zombieHandler) {
  127.     zombieHandler->Remove();
  128.     zombieHandler= 0;
  129.     }
  130.     PerformCommand(gResetUndo);
  131.     SafeDelete(pttycon);
  132.     SafeDelete(pttycstr);
  133.     SafeDelete(noEchoBuffer);
  134.     SafeDelete(inpQueue);
  135. }
  136.  
  137. Command *ShellTextView::DoKeyCommand(int ch, Token token)
  138. {
  139.     if (pttycon == 0)
  140.     return gNoChanges;
  141.  
  142.     if (ModifiesReadOnly())
  143.     SetSelection(text->End(), text->End(), TRUE);
  144.     if (pttycon->RawOrCBreak()) {
  145.     char c= ch;
  146.     pttycon->SubmitToSlave(&c, 1);
  147.     lastSubmit= text->End();
  148.     if (pttycon->Echo())
  149.         return TextView::DoKeyCommand(ch, token);
  150.     return gResetUndo;
  151.     }
  152.     if (strchr(pttycstr,ch)) {
  153.     GapText buf(4096), *tosend;
  154.     byte *cbuf;
  155.     int n;
  156.     
  157.     if (ch == pttyc.intr || ch == pttyc.quit) {
  158.         SubmitInterrupt(ch);
  159.         return gResetUndo;
  160.     }
  161.     if (pttycon->Echo()) {
  162.         buf.ReplaceRange(0, buf.End(), text, outputCursor->Pos(), text->End());
  163.         tosend= &buf;
  164.     } else 
  165.         tosend= noEchoBuffer;
  166.     tosend->AddChar(tosend->End(), ch);
  167.     n= tosend->End();
  168.     cbuf= tosend->GetLineAccess(&linebuf, 0, n);
  169.     if (tosend == noEchoBuffer)
  170.         noEchoBuffer->Empty();
  171.     Submit((char*) cbuf, n);
  172.     return gNoChanges;
  173.     }
  174.     if (!pttycon->Echo()) {
  175.     noEchoBuffer->Append(ch);
  176.     return gNoChanges;
  177.     }
  178.     if (text->IsKindOf(StyledText) && ch != '\b') {
  179.     int f, t;
  180.     GetSelection(&f,&t);
  181.     StyledText *st= (StyledText*) text;
  182.     st->SetCharStyle(eTxTPFace, f, t,
  183.           CharStyleSpec(eFontDefault, eFaceBold, 0, 0, FALSE));
  184.     }
  185.     if (Iscntrl(ch) && ch != '\b' && ch != '\t')
  186.     return CntrlChar((byte)ch);
  187.     return TextView::DoKeyCommand(ch, token);
  188. }
  189.  
  190. char *ShellTextView::GetTermCap()
  191. {
  192.     return "etterm|etterm dumb terminal emulator:bs";
  193. }
  194.  
  195. char *ShellTextView::GetTerm()
  196. {
  197.     return "etterm";
  198. }
  199.  
  200. void ShellTextView::Received()
  201. {
  202.     char buf[cPttyBufSize];
  203.     int n= 0, f, t, upto;
  204.     
  205.     Focus();
  206.     inReceive= TRUE;
  207.     GetSelection(&f, &t);
  208.     inputCursor->ChangeMark(f, t-f); 
  209.     outputCursor->Unlock();
  210.     if (pttycon)
  211.     n= pttycon->Read(buf, sizeof(buf)-1);
  212.     
  213.     if (n <= 0)     // EOT from child or lost connection
  214.     SlaveDied();
  215.     else {  
  216.     GapText gt(n);
  217.     HideSelection(FALSE);
  218.     SetNoBatch(pttycon->RawOrCBreak());
  219.     ProcessOutput(>, buf, n);
  220.     if (outputCursor->Pos() < lastSubmit)
  221.         upto= Math::Min(lastSubmit, text->End());
  222.     else 
  223.         upto= outputCursor->Pos();
  224.     InsertReceivedText(>, outputCursor->Pos(), upto);
  225.     if (outputCursor->Pos() > maxLogSize) 
  226.         Wrap();
  227.  
  228.     SetSelection(inputCursor->Pos(), inputCursor->End());
  229.     }
  230.     if (doReveal) { 
  231.     int p= outputCursor->Pos();
  232.     Rectangle r= BoundingRect(p, p).Expand(8);
  233.     RevealOutput(r, r.extent);
  234.     }
  235.     outputCursor->Lock();
  236.     PerformCommand(gResetUndo);
  237.     inReceive= FALSE;
  238.     FlushPendingInput();
  239. }
  240.  
  241. void ShellTextView::FlushPendingInput()
  242. {
  243.     if (inpQueue->Size()) {
  244.     ByteArray *bp;
  245.     Iter next(inpQueue);
  246.     while (bp= (ByteArray*)next()) {
  247.         TtyInput((char*)bp->Str());
  248.         inpQueue->RemovePtr(bp);
  249.         SafeDelete(bp);
  250.     }
  251.     }
  252. }
  253.  
  254. void ShellTextView::InsertReceivedText(Text *t, int from, int to)
  255. {
  256.     if (!text->IsKindOf(StyledText))
  257.     text->Paste(t, from, to);
  258.     else {    
  259.     StyledText *st= (StyledText*) text;
  260.     int upto, ostart= outputCursor->Pos();
  261.     st= (StyledText*) text;
  262.     st->SetCharStyle(eTxTPFace, from, to, CharStyleSpec(eFontDefault, eFacePlain, 0));
  263.     text->Paste(t, from, to);
  264.     if (ostart <= lastSubmit && !pttycon->RawOrCBreak()) {
  265.         upto= Math::Min(Math::Min(lastSubmit, outputCursor->Pos()), text->End());
  266.         st->SetCharStyle(eTxTPFace, ostart, upto, CharStyleSpec(eFontDefault, eFaceBold,0));
  267.         st->SetCharStyle(eTxTPFace, upto, upto, CharStyleSpec(eFontDefault, eFacePlain,0));
  268.     }
  269.     }
  270. }
  271.  
  272. void ShellTextView::ProcessOutput(GapText *gt, char *buf, int n)
  273. {
  274.     for (char *p= buf; p < buf+n; p++) 
  275.     if (HandleSpecialOutChar(*p, gt)) 
  276.         gt->AddChar(gt->End(), *p);
  277. }
  278.  
  279. void ShellTextView::RevealOutput(Rectangle r, Point p)
  280. {
  281.     RevealRect(r, p);
  282. }
  283.  
  284. bool ShellTextView::HandleSpecialOutChar(char p, GapText *t)
  285. {
  286.     if (state) {
  287.     state= FALSE;
  288.     return FALSE;
  289.     } 
  290.     if (pendingCR) {
  291.     if (p == '\r')
  292.         return FALSE;
  293.     if (p == '\n')
  294.         pendingCR= FALSE;
  295.     else {
  296.         CarriageReturn(t);
  297.         pendingCR= FALSE;
  298.     }
  299.     }
  300.     
  301.     switch (p) {
  302.     case cESC: // during a more(1) spurious ESC '9's appear
  303.     state= TRUE;
  304.     return FALSE;
  305.     case '\r':
  306.     pendingCR= TRUE;                         
  307.     break;
  308.     case '\b':
  309.     BackSpace(t, 1);    
  310.     break;
  311.     case '\007':
  312.     gWindow->Bell();
  313.     break;
  314.     default:
  315.     return TRUE;
  316.     }
  317.     return FALSE;
  318. }
  319.  
  320. void ShellTextView::Submit(char *buf, int n)
  321. {
  322.     if (n && pttycon && pttycon->SubmitToSlave(buf, n))
  323.     ShowAlert(eAlertNote, "Pty command buffer overflow\nlast command ignored");
  324.     lastSubmit= text->End();
  325. }
  326.  
  327. void ShellTextView::SubmitInterrupt(char ch)
  328. {
  329.     if (pttycon && pttycon->SubmitToSlave(&ch, 1))
  330.     pttycon->SubmitToSlave(&ch, 1); // try again after flush of buffers
  331. }
  332.  
  333. void ShellTextView::Done()
  334. {
  335.     if (pttycon)
  336.     pttycon->KillChild();
  337.  
  338. bool ShellTextView::IsIdle()
  339. {
  340.     return pttycon == 0;
  341.  
  342. void ShellTextView::SlaveDied()
  343. {
  344.     if (pttycon) 
  345.     CleanupDeath();
  346.     zombieHandler= 0;
  347. }
  348.  
  349. void ShellTextView::CleanupDeath()
  350. {
  351.     if (pttyinp) {
  352.     pttyinp->Remove();
  353.     pttyinp= 0;
  354.     }
  355.     ClearOutput();
  356.     SafeDelete(pttycon);
  357. }
  358.  
  359. void ShellTextView::DrawOutputCursor(Point p)
  360. {
  361.     Point hotspot(-crevCaretHeight, 0);
  362.     Rectangle r(p+hotspot, Point(7,4));
  363.     r.origin+= GetInnerOrigin();
  364.     
  365.     if (GrHasColor()) {
  366.     if (gCaretColor == 0)
  367.         gCaretColor= new_RGBColor(0, 0, 255);
  368.     GrPaintBitMap(r, gRevCaretImage, gCaretColor);
  369.     } else
  370.     GrPaintBitMap(r, gRevCaretImage, ePatXor);
  371. }
  372.  
  373. void ShellTextView::Draw(Rectangle r)
  374. {
  375.     Point p;
  376.     int l;
  377. //    if (!pttycon)
  378. //        GrPaintRect(r, ePatGrey25);
  379.     TextView::Draw(r);
  380.     if (pttycon && pttycon->RawOrCBreak())
  381.     return;
  382.     CharToPoint(outputCursor->Pos(), &l, &p);
  383.     DrawOutputCursor(p);
  384. }
  385.  
  386. void ShellTextView::Paste(Text *insert)
  387. {
  388.     if (ModifiesReadOnly())
  389.     return;
  390.     if (pttycon && pttycon->RawOrCBreak()) {
  391.     AutoTextIter next(insert);
  392.     Token t;
  393.     int ch;
  394.     while ((ch= next()) != cEOT) {
  395.         t.Code= (short)ch;
  396.         DoKeyCommand((short)ch, t);
  397.     }
  398.     } else
  399.     TextView::Paste(insert);
  400. }
  401.  
  402. bool ShellTextView::ModifiesReadOnly()
  403. {
  404.     int f, t;
  405.     GetSelection(&f, &t);
  406.     return f < outputCursor->Pos();
  407. }
  408.     
  409. bool ShellTextView::DeleteRequest(int from, int)
  410. {
  411.     return from >= outputCursor->Pos();
  412. }
  413.  
  414. void ShellTextView::BecomeConsole()
  415. {
  416.     if (pttycon)
  417.     pttycon->BecomeConsole();
  418. }
  419.  
  420. void ShellTextView::BackSpace(Text *t, int n)
  421. {
  422.     int s= t->End();
  423.     if (s > 0) {
  424.     int d1= Math::Min(s, n);
  425.     t->Cut(s-d1, s);
  426.     }
  427.     if (n > s) {
  428.     int d2= n-s;
  429.     lastSubmit= Math::Max(0, lastSubmit-d2);
  430.     int p= outputCursor->Pos();
  431.     outputCursor->ChangeMark(Math::Max(0, p-d2), 0);
  432.     text->Cut(p-d2,p);
  433.     }
  434. }
  435.  
  436. void ShellTextView::CarriageReturn(Text *t)
  437. {
  438.     int s= t->End(), ch;
  439.     pendingCR= FALSE;
  440.     while (s > 0) {
  441.     int ch= (*t)[s];
  442.     if (ch == '\n' || ch == '\r') {
  443.         t->Cut(s+1,t->End());
  444.         return;
  445.     }
  446.     s--;
  447.     }
  448.     t->Empty();
  449.     s= text->End();
  450.     int at= s-1;
  451.     while (at >= 0) {
  452.     ch= (*text)[at];
  453.     if (ch == '\n' || ch == '\r') {
  454.         outputCursor->ChangeMark(at+1, 0);
  455.         text->Cut(at+1, s);
  456.         lastSubmit= at+1;
  457.         return;
  458.     }
  459.     at--;
  460.     }
  461. }
  462.  
  463. Command *ShellTextView::CntrlChar(byte b)
  464. {
  465.     GapText gt((byte*)"^");
  466.     gt.Append(b+'@');
  467.     return InsertText(>);
  468. }
  469.  
  470. void ShellTextView::Wrap()
  471. {
  472.     int line, del, at= outputCursor->Pos();
  473.     line= CharToLine(Math::Max(0, text->End()-cLowWaterMark));
  474.     del= StartLine(line);
  475.     lastSubmit-= del;
  476.     text->Cut(0, del);
  477. }
  478.  
  479. Text *ShellTextView::SetText(Text *t, bool revealTop)
  480. {
  481.     Text *ot= TextView::SetText(t, revealTop);
  482.     if (ot) {
  483.     ot->RemoveMark(outputCursor);
  484.     ot->RemoveMark(inputCursor);
  485.     }
  486.     t->AddMark(outputCursor);
  487.     t->AddMark(inputCursor);
  488.     MarksToEOT();
  489.     return ot;
  490. }
  491.  
  492. void ShellTextView::MarksToEOT()
  493. {
  494.     Text *t= GetText();
  495.     outputCursor->ChangeMark (t->End(), 0);
  496.     outputCursor->Lock();
  497.     inputCursor->ChangeMark (t->End(), 0);
  498.     lastSubmit= t->End();
  499. }
  500.  
  501. void ShellTextView::ClearOutput()
  502. {
  503.     text->Cut(0, text->End());
  504.     MarksToEOT();
  505.     SetSelection(0,0,FALSE);
  506.     RevealSelection();
  507. }
  508.  
  509. void ShellTextView::DoReveal(bool b)
  510. {
  511.     if (!doReveal && b)
  512.     RevealSelection();
  513.     doReveal= b;
  514. }
  515.  
  516. void ShellTextView::Start(char *name, char **args, bool cons)
  517. {
  518.     if (pttycon != 0)
  519.     Error("Start", "shell is already running");
  520.     
  521.     state= 0;
  522.     MarksToEOT();
  523.     if (name == 0)
  524.     name= gDefaultArgv[0];
  525.     argv= args;
  526.     if (args == 0)
  527.     argv= gDefaultArgv;
  528.     pttycon= gSystem->MakePttyConnection(name, argv);
  529.     if (!pttycon)
  530.     Error("Start", "cannot establish connection with tty");
  531.     if (cons)
  532.     BecomeConsole(); 
  533.     if (pttycon->GetFile() == 0)
  534.     Error("Start", "could not spawn slave pseudo tty");  
  535.     pttycon->GetPttyChars(&pttyc);
  536.     pttycstr= strprintf("\n\r%c%c%c%c%c", pttyc.rprnt, pttyc.susp, 
  537.                            pttyc.intr, pttyc.quit,
  538.                            pttyc.eof);
  539.     SetStopChars(pttycstr);
  540.     gSystem->AddFileInputHandler(
  541.            pttyinp= new PttyInputHandler(pttycon->GetFile(), this));
  542.     gSystem->AddZombieHandler(
  543.            zombieHandler= new ShellZombieHandler(pttycon->GetPid(), this));       
  544. }
  545.  
  546. void ShellTextView::Reconnect() 
  547. {
  548.     if (pttycon) {
  549.      switch (ShowAlert(eAlertCaution, "There is alreay a shell connection.\nForce reconnect?")) {
  550.         case cIdYes:
  551.             pttycon= 0;
  552.             break;
  553.         case cIdNo:
  554.         case cIdCancel:
  555.             return;
  556.         }
  557.     }    
  558.     Start(argv[0], argv);
  559.     ForceRedraw();
  560. }
  561.  
  562. Menu *ShellTextView::MakeMenu(int menuId)
  563. {
  564.     Menu *m;
  565.     
  566.     if (menuId == cSHELLMENU) {
  567.     m= new Menu(cSHELLMENU, "Shell");
  568.     m->AppendItems( 
  569.             "Auto Reveal On",   cAUTOREVEAL,
  570.             "Reconnect",        cRECONNECT,
  571.             "Become Console",   cBECOMECONSOLE,
  572.             "Do It",            cDOIT,
  573.             0);
  574.     }
  575.     else
  576.     m= TextView::MakeMenu(menuId);
  577.     return m;
  578. }
  579.  
  580. void ShellTextView::DoSetupMenu(Menu *m)
  581. {
  582.     char *current;
  583.  
  584.     TextView::DoSetupMenu(m);
  585.     if (ModifiesReadOnly()) {
  586.     m->DisableItem(cCUT);
  587.     m->DisableItem(cPASTE);
  588.     }
  589.     if (doReveal)
  590.     current= "Auto reveal off";
  591.     else
  592.     current= "Auto reveal on";
  593.  
  594.     m->ReplaceItem(cAUTOREVEAL, current);
  595.     if (pttycon)
  596.     m->EnableItem(cBECOMECONSOLE);
  597.     m->EnableItem(cRECONNECT);
  598.     m->EnableItems(cCLEAR, cAUTOREVEAL, 0);
  599.     if (!Caret())
  600.     m->EnableItem(cDOIT);
  601. }
  602.     
  603. Command *ShellTextView::DoMenuCommand(int cmd)
  604. {
  605.     switch (cmd) {
  606.  
  607.     case cAUTOREVEAL:
  608.     DoReveal(!doReveal);
  609.     break;
  610.     
  611.     case cCLEAR:
  612.     ClearOutput();
  613.     return gResetUndo;
  614.     
  615.     case cRECONNECT:
  616.     Reconnect();
  617.     break;
  618.     
  619.     case cBECOMECONSOLE:
  620.     pttycon->BecomeConsole();
  621.     break;
  622.  
  623.     case cDOIT:
  624.     Doit();
  625.     return gResetUndo;
  626.     }
  627.     return TextView::DoMenuCommand(cmd);
  628. }
  629.  
  630. void ShellTextView::SelectAll(bool redraw)
  631. {
  632.     SetSelection(outputCursor->Pos(), text->End(), redraw);
  633.     RevealSelection();
  634. }
  635.  
  636. void ShellTextView::Doit()
  637. {
  638.     int f, t;
  639.     GapText buf(256);
  640.     Token token;
  641.     
  642.     GetSelection(&f, &t);
  643.     text->Copy(&buf,f, t);
  644.     SetSelection(text->End(), text->End());
  645.     Paste(&buf);
  646.     DoKeyCommand('\n', token);
  647. }
  648.  
  649. char **ShellTextView::GetArgv()
  650.     return argv; 
  651. }
  652.     
  653. PttyConnection *ShellTextView::GetPtty()
  654. {
  655.     return pttycon;
  656. }
  657.  
  658. void ShellTextView::SetTtySize(Rectangle r, Font *fd)
  659. {
  660.     int cols= r.extent.x / fd->Width(' ');
  661.     Metric m= MeasureText(0, fd, r.extent.x, 1);
  662.     int rows= r.extent.y / m.Height(); 
  663.     SetTtySize(rows, cols);
  664. }
  665.  
  666. void ShellTextView::ChangedViewRect(Rectangle r)
  667. {
  668.     SetTtySize(r, text->GetFont(0)); 
  669.     if (Width() != r.Width())
  670.     SetWidth(r.Width());
  671. }
  672.  
  673. void ShellTextView::SetTtySize(int rows, int cols)
  674. {
  675.     if (pttycon)
  676.     pttycon->SetSize(rows, cols);
  677. }
  678.  
  679. void ShellTextView::SetMaxLogSize(int n)
  680. {
  681.     maxLogSize= n;
  682. }
  683.  
  684. Command *ShellTextView::TtyInput(char *buf, int len)
  685. {
  686.     Token t;
  687.     
  688.     if (inReceive) {
  689.     inpQueue->Add(new ByteArray(buf, len));
  690.     return gNoChanges;
  691.     }
  692.     Command *cmd= gNoChanges;
  693.     if (len == -1)
  694.     len= strlen(buf);
  695.     for (char *cp= buf; cp < buf+len; cp++) {
  696.     t.Code= (short)*cp;
  697.     cmd= DoKeyCommand((int)*cp, t);
  698.     }
  699.     PerformCommand(gResetUndo);
  700.     return cmd;
  701. }
  702.  
  703. OStream& ShellTextView::PrintOn(OStream &s)
  704. {
  705.     TextView::PrintOn(s);
  706.     return s << doReveal SP;
  707. }
  708.  
  709. IStream& ShellTextView::ReadFrom(IStream &s)
  710. {
  711.     TextView::ReadFrom(s);
  712.     Term();
  713.     s >> Bool(doReveal);
  714.     Init(doReveal, gDefaultArgv[0], gDefaultArgv, FALSE);
  715.     MarksToEOT();
  716.     return s;
  717. }
  718.